
import UIKit
import Foundation
import Kingfisher

enum ButtonEdgeInsetsStyle {
    case Top
    case Left
    case Right
    case Bottom
}

extension UIButton {
    
    //MARK: 按钮加载网络图片
    /// 按钮加载网络图片
    /// - Parameters:
    ///   - webUrl: <#webUrl description#>
    ///   - placeholder: <#placeholder description#>
    func loadImage(_ webUrl:String?, placeholder:DefaultImageType = .none){
        if webUrl?.isValidURL() == false || webUrl == nil {
            self.setImage(UIImage(named: placeholder.rawValue), for: .normal)
            return
        }
        let url = URL(string: webUrl!)
        //PPLog(url)
        if url?.pathExtension == "gif" {
            //加载gif动图
            KF.url(url, cacheKey: "\(url)-imageview").set(to: self.imageView!)
        }else{
            //加载普通图
            let progressive = ImageProgressive(
                isBlur: true,
                isFastestScan: true,
                scanInterval: 0.1
            )
            self.kf.setImage(
                with: url,
                for: .normal,
                placeholder: UIImage(named: placeholder.rawValue),
                options: [.loadDiskFileSynchronously,
                          .progressiveJPEG(progressive)],
                progressBlock: { receivedSize, totalSize in
                    //PPLog("图片下载中：\(receivedSize)/\(totalSize)， 地址：\(String(describing: webUrl))")
                },
                completionHandler: { result in
                    //PPLog("Finished \(result)")
            }
            )
        }
    }
}

//MARK: - 按钮角标
private var UIButton_badgeKey : Void?
private var UIButton_badgeBGColorKey : Void?
private var UIButton_badgeTextColorKey : Void?
private var UIButton_badgeFontKey : Void?
private var UIButton_badgePaddingKey : Void?
private var UIButton_badgeMinSizeKey : Void?
private var UIButton_badgeOriginXKey : Void?
private var UIButton_badgeOriginYKey : Void?
private var UIButton_shouldHideBadgeAtZeroKey : Void?
private var UIButton_shouldAnimateBadgeKey : Void?
private var UIButton_badgeValueKey : Void?

extension UIButton {
    fileprivate var badgeLabel: UILabel? {
        get {
            return  objc_getAssociatedObject(self, &UIButton_badgeKey) as? UILabel
        }
        set {
            objc_setAssociatedObject(self, &UIButton_badgeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        
    }
    /**
     * 角标值
     */
    var badgeValue : String?  {
        get{
            return objc_getAssociatedObject(self, &UIButton_badgeValueKey) as? String
        }

        set (badgeValue){
                objc_setAssociatedObject(self, &UIButton_badgeValueKey, badgeValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

            if (badgeValue?.isEmpty)!   || (badgeValue == "") || ((badgeValue == "0") && shouldHideBadgeAtZero) {
                    removeBadge()
                } else if (self.badgeLabel == nil ) {
                    self.badgeLabel  = UILabel(frame: CGRect(x: self.badgeOriginX , y: self.badgeOriginY, width: 20, height: 20))
                    self.badgeLabel?.textColor = self.badgeTextColor
                    self.badgeLabel?.backgroundColor = self.badgeBGColor
                    self.badgeLabel?.font = self.badgeFont
                    self.badgeLabel?.textAlignment = .center
                    badgeInit()
                    addSubview(self.badgeLabel!)
                    updateBadgeValue(animated: false)
                } else {
                    updateBadgeValue(animated: true)
                }
        }

    }

    /**
     * Badge background color
     */
    var badgeBGColor: UIColor? {
        get {
            return objc_getAssociatedObject(self, &UIButton_badgeBGColorKey) as? UIColor ?? .red
        }
        set {
            objc_setAssociatedObject(self, &UIButton_badgeBGColorKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { refreshBadge() }
        }
    }

    /**
     * Badge text color
     */
    var badgeTextColor: UIColor? {
        get{
            return objc_getAssociatedObject(self, &UIButton_badgeTextColorKey) as? UIColor ?? .white
        }
        set{
            objc_setAssociatedObject(self, &UIButton_badgeTextColorKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) {  refreshBadge() }
        }
    }
    
    /**
     * Badge font
     */
    var badgeFont: UIFont? {
        get {
            return objc_getAssociatedObject(self, &UIButton_badgeFontKey) as? UIFont ?? UIFont.systemFont(ofSize: 12)
        }
        set{
            objc_setAssociatedObject(self, &UIButton_badgeFontKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { refreshBadge() }
        }
    }

    /**
     *  Padding value for the badge
     */
    var badgePadding: CGFloat {
        get{
            return  objc_getAssociatedObject(self, &UIButton_badgePaddingKey) as? CGFloat ?? 6
        }
        set{
            objc_setAssociatedObject(self, &UIButton_badgePaddingKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { updateBadgeFrame() }
        }
    }

    /**
     * badgeLabel 最小尺寸
     */
    var badgeMinSize: CGFloat {
        get{
            return objc_getAssociatedObject(self, &UIButton_badgeMinSizeKey) as? CGFloat ?? 8
        }
        set{
            objc_setAssociatedObject(self, &UIButton_badgeMinSizeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { updateBadgeFrame() }
        }
    }

    /**
     *  badgeLabel OriginX
     */
    var badgeOriginX: CGFloat {
        get{
            return objc_getAssociatedObject(self, &UIButton_badgeOriginXKey) as? CGFloat ?? 0
        }
        set{
            objc_setAssociatedObject(self, &UIButton_badgeOriginXKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) {
                updateBadgeFrame()
            }
        }
    }

    /**
     * badgeLabel OriginY
     */
    var badgeOriginY: CGFloat  {
        get{
            return objc_getAssociatedObject(self, &UIButton_badgeOriginYKey) as? CGFloat ?? -5
        }
        set{
            objc_setAssociatedObject(self, &UIButton_badgeOriginYKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if (self.badgeLabel != nil) { updateBadgeFrame() }
        }
    }

    /**
     * In case of numbers, remove the badge when reaching zero
     */
    var shouldHideBadgeAtZero: Bool  {
        get {
            return objc_getAssociatedObject(self, &UIButton_shouldHideBadgeAtZeroKey) as? Bool ?? true
        }
        set {
            objc_setAssociatedObject(self, &UIButton_shouldHideBadgeAtZeroKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    /**
     * Badge has a bounce animation when value changes
     */
    var shouldAnimateBadge: Bool {
        get{
            return objc_getAssociatedObject(self, &UIButton_shouldAnimateBadgeKey) as? Bool ?? true
        }
        set{
            objc_setAssociatedObject(self, &UIButton_shouldAnimateBadgeKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    
    fileprivate func badgeInit()  {
        if let label = self.badgeLabel {
            self.badgeOriginX = self.frame.size.width - label.frame.size.width/2
        }
        
        self.clipsToBounds = false
    }

    fileprivate func refreshBadge() {
        guard let tempLabel = self.badgeLabel else { return }
        tempLabel.textColor = self.badgeTextColor
        tempLabel.backgroundColor  = self.badgeBGColor
        tempLabel.font  = self.badgeFont
    }

    fileprivate func removeBadge() {
        UIView.animate(withDuration: 0.2, animations: {
            self.badgeLabel?.transform = CGAffineTransform.init(scaleX: 0, y: 0)
        }) { (finished: Bool) in
            self.badgeLabel?.removeFromSuperview()
            if (self.badgeLabel != nil) { self.badgeLabel = nil }
        }
    }
    
    fileprivate func updateBadgeValue(animated: Bool) {
        if animated && self.shouldAnimateBadge && !(self.badgeLabel?.text == self.badgeValue) {
            let animation = CABasicAnimation(keyPath: "transform.scale")
            animation.fromValue = 1.5
            animation.toValue = 1
            animation.duration = 0.2
            animation.timingFunction = CAMediaTimingFunction(controlPoints: 0.4, 1.3, 1.0, 1.0)
            self.badgeLabel?.layer.add(animation, forKey: "bounceAnimation")
        }

        var badgeValue = 0
        if let badgeStr = self.badgeValue , let value = Int(badgeStr) {
                badgeValue = value
        }
        self.badgeLabel?.text = badgeValue >= 99 ? "99+" : self.badgeValue
        self.badgeLabel?.text =  self.badgeValue
        let duration: TimeInterval = (animated && self.shouldAnimateBadge) ? 0.2 : 0
        UIView.animate(withDuration: duration, animations: {
            self.updateBadgeFrame()
        })
    }

   fileprivate  func updateBadgeFrame() {
        let expectedLabelSize: CGSize = badgeExpectedSize()
        var minHeight: CGFloat = expectedLabelSize.height
        minHeight = (minHeight < badgeMinSize) ? badgeMinSize : expectedLabelSize.height
       
        var minWidth: CGFloat = expectedLabelSize.width
        let padding = self.badgePadding
        minWidth = (minWidth < minHeight) ? minHeight : expectedLabelSize.width
    
        
        self.badgeLabel?.frame = CGRect(x: self.badgeOriginX, y: self.badgeOriginY, width: minWidth + padding, height: minHeight + padding)
        self.badgeLabel?.layer.cornerRadius = (minHeight + padding) / 2
        self.badgeLabel?.layer.masksToBounds = true
    }

    fileprivate func badgeExpectedSize() -> CGSize {
        let frameLabel: UILabel = duplicate(self.badgeLabel)
        frameLabel.sizeToFit()
        let expectedLabelSize: CGSize = frameLabel.frame.size
        return expectedLabelSize
    }

    fileprivate func duplicate(_ labelToCopy: UILabel? ) -> UILabel {
        guard let temp = labelToCopy else { fatalError("xxxx") }
        let duplicateLabel = UILabel(frame: temp.frame )
        duplicateLabel.text = temp.text
        duplicateLabel.font = temp.font
        return duplicateLabel
    }


}

//MARK: - 扩大按钮点击范围
/*
 扩大按钮点击范围
 btn.clickScopeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
 */
extension UIButton{
    /// 改进写法【推荐】
    private struct RuntimeKey {
        static let clickEdgeInsets = UnsafeRawPointer.init(bitPattern: "clickEdgeInsets".hashValue)
    }
    
    /// 点击范围向外四边延伸距离
    public var clickScopeInsets: UIEdgeInsets? {
        set {
            objc_setAssociatedObject(self, UIButton.RuntimeKey.clickEdgeInsets!, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY)
        }
        get {
            return objc_getAssociatedObject(self, UIButton.RuntimeKey.clickEdgeInsets!) as? UIEdgeInsets ?? UIEdgeInsets.zero
        }
    }
    
    /// 重写系统方法修改点击区域
    open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        super.point(inside: point, with: event)
        var bounds = self.bounds
        guard let clickScopeInsets = clickScopeInsets else {
            return false
        }
        let x: CGFloat = -(clickScopeInsets.left)
        let y: CGFloat = -(clickScopeInsets.top)
        let width: CGFloat = bounds.width + clickScopeInsets.left + clickScopeInsets.right
        let height: CGFloat = bounds.height + clickScopeInsets.top + clickScopeInsets.bottom
        bounds = CGRect(x: x, y: y, width: width, height: height) //负值是方法响应范围
        return bounds.contains(point)
    }
}

extension UIButton {
    ///快捷生成UIButton
    convenience init(_ fontSize:CGFloat, weight:UIFont.Weight = .regular, textColor:UIColor = .black, text:String? = nil, align:UIControl.ContentHorizontalAlignment = .center) {
        self.init()
        self.setTitleColor(textColor, for:.normal)
        let font = UIFont.systemFont(ofSize:fontSize,weight:weight)
        self.titleLabel?.font = font
        self.setTitle(text, for: .normal)
        self.contentHorizontalAlignment = align
    }
    
    /// 一行快速生成按钮对象
    /// - Parameters:
    ///   - frame: <#frame description#>
    ///   - icon: <#icon description#>
    ///   - backImg: <#backImg description#>
    ///   - text: <#text description#>
    ///   - textColor: <#textColor description#>
    ///   - font: <#font description#>
    convenience init(frame:CGRect? = .zero, icon:String? = nil, backImg:String? = nil, text:String? = nil, textColor:UIColor = .black, bgColor:UIColor = .clear, font:UIFont? = .systemFont(ofSize: 15, weight: .regular)) {
        self.init()
        if let rect = frame {
            self.frame = rect
        }
        if let imageName = icon {
            setImage(UIImage(named: imageName), for: .normal)
        }
        if let backImgName = backImg {
            setBackgroundImage(UIImage(named: backImgName), for: .normal)
        }
        if let title = text {
            setTitle(title, for: .normal) 
        }
        setTitleColor(textColor, for:.normal)
        backgroundColor = bgColor
        if let oFont = font {
            titleLabel?.font = oFont
        }
    }
    
    /// 一行快速生成按钮对象带事件
    /// - Parameters:
    ///   - frame: <#frame description#>
    ///   - icon: <#icon description#>
    ///   - backImg: <#backImg description#>
    ///   - text: <#text description#>
    ///   - textColor: <#textColor description#>
    ///   - font: <#font description#>
    ///   - target: <#target description#>
    ///   - action: <#action description#>
    convenience init(frame:CGRect? = .zero, icon:String? = nil, backImg:String? = nil, text:String? = nil, textColor:UIColor = .black, bgColor:UIColor = .clear, font:UIFont? = .systemFont(ofSize: 15, weight: .regular), tag:Int? = nil, target:Any? = nil, action:Selector) {
        self.init()
        if let tagId = tag {
            self.tag = tagId
        }
        if let rect = frame {
            self.frame = rect
        }
        if let imageName = icon {
            setImage(UIImage(named: imageName), for: .normal)
        }
        if let backImgName = backImg {
            setBackgroundImage(UIImage(named: backImgName), for: .normal)
        }
        if let title = text {
            setTitle(title, for: .normal)
        }
        setTitleColor(textColor, for:.normal)
        backgroundColor = bgColor
        if let oFont = font {
            titleLabel?.font = oFont
        }
        addTarget(target, action: action, for: .touchUpInside)
    }
    
    func setTitle(_ title:String?,selectedTitle:String?=nil) {
        self.setTitle(title, for:.normal)
        if let selected = selectedTitle {
            self.setTitle(selected, for: .selected)
        }
    }
    
    func setTitle(_ title:String?, style: ButtonEdgeInsetsStyle, imageTitleSpace: CGFloat) {
        setTitle(title)
        layoutButton(style: style, imageTitleSpace: imageTitleSpace)
    }
    
    func setTitleColor(_ color:UIColor?,selectedColor:UIColor?=nil) {
        self.setTitleColor(color, for: .normal)
        if let selected = selectedColor {
            self.setTitleColor(selected, for: .selected)
        }
    }
    
    /// 按钮文字换成行显示
    /// titles: 数组小于等于0，不做操作
    /// fontSizes、weights、colors: 属性参数数组长度小于titles的长度，默认取下标第0个
    func buttonWrapAssignment(titles:[String],
                              fontSizes:[CGFloat] = [16],
                              weights:[UIFont.Weight] = [UIFont.Weight.regular],
                              colors:[UIColor] = [UIColor.black],
                              spacing:CGFloat = 6){
        
        if titles.count <= 0 { return }
        
        titleLabel?.textAlignment = .center
        titleLabel?.lineBreakMode = .byWordWrapping
        
        let paraStyle = NSMutableParagraphStyle()
        paraStyle.lineSpacing = spacing
        paraStyle.lineBreakMode = .byWordWrapping
        paraStyle.alignment = .center
        
        let totalString = titles.joined(separator: "\n")
        let attString = NSMutableAttributedString(string: totalString)
        attString.addAttribute(.paragraphStyle, value: paraStyle, range: totalString.exMatchStrRange(totalString))
        for (index, item) in titles.enumerated() {
            var fontSize:CGFloat = 16
            var weight = UIFont.Weight.regular
            var color = UIColor.black
            if fontSizes.count < titles.count && fontSizes.count > 0 {
                fontSize = fontSizes[0]
            }else if fontSizes.count >= titles.count && fontSizes.count > 0 {
                fontSize = fontSizes[index]
            }
            if weights.count < titles.count {
                weight = weights[0]
            }else if weights.count >= titles.count && weights.count > 0 {
                weight = weights[index]
            }
            if colors.count < titles.count && colors.count > 0 {
                color = colors[0]
            }else if colors.count >= titles.count && colors.count > 0 {
                color = colors[index]
            }
            attString.addAttributes([.font:UIFont.systemFont(ofSize:fontSize,weight:weight),
                                     .foregroundColor:color], range: totalString.exMatchStrRange(item))
        }
        setAttributedTitle(attString, for: .normal)
    }
    
    convenience init(imageName:String) {
        self.init()
        self.setImage(imageName,selectedName:nil)
    }
    
    func normalTitle(_ title:String) -> UIButton {
        self.setTitle(title, for:.normal)
        return self
    }
    
    /// 设置按钮图标
    /// - Parameters:
    ///   - name: 图标名称，正常模式
    ///   - selectedName: 图标名称，选中模式
    func setImage(_ name:String?,selectedName:String? = nil) {
        self.setImage(UIImage(named: name ?? ""), for:.normal)
        if let selected = selectedName {
            self.setImage(UIImage(named:selected), for:.selected)
        }
    }
    
    func setImageWithTintColor(_ tintColor:UIColor,imageName:String) {
        self.tintColor = tintColor
        let image = UIImage(named:imageName)?.withRenderingMode(.alwaysTemplate)
        self.setImage(image,for:.normal)
    }
    
    func addTarget_u(_ target:Any?,action:Selector) {
        addTarget(target,action:action,for:.touchUpInside)
    }
    
    func removeTarget_u(_ target:Any?,action:Selector) {
        removeTarget(target,action:action,for:.touchUpInside)
    }
    
    ///UIButton图片文字排列
    func layoutButton(style: ButtonEdgeInsetsStyle, imageTitleSpace: CGFloat) {
        //得到imageView和titleLabel的宽高
        let imageWidth = self.imageView?.frame.size.width
        let imageHeight = self.imageView?.frame.size.height
        
        var labelWidth: CGFloat! = 0.0
        var labelHeight: CGFloat! = 0.0
        
        labelWidth = self.titleLabel?.intrinsicContentSize.width
        labelHeight = self.titleLabel?.intrinsicContentSize.height
        
        //初始化imageEdgeInsets和labelEdgeInsets
        var imageEdgeInsets = UIEdgeInsets.zero
        var labelEdgeInsets = UIEdgeInsets.zero
        
        //根据style和space得到imageEdgeInsets和labelEdgeInsets的值
        switch style {
        case .Top:
            //上 左 下 右
            imageEdgeInsets = UIEdgeInsets(top: -labelHeight-imageTitleSpace/2, left: 0, bottom: 0, right: -labelWidth)
            labelEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth!, bottom: -imageHeight!-imageTitleSpace/2, right: 0)
            break;
            
        case .Left:
            imageEdgeInsets = UIEdgeInsets(top: 0, left: -imageTitleSpace/2, bottom: 0, right: imageTitleSpace)
            labelEdgeInsets = UIEdgeInsets(top: 0, left: imageTitleSpace/2, bottom: 0, right: -imageTitleSpace/2)
            break;
            
        case .Bottom:
            imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: -labelHeight!-imageTitleSpace/2, right: -labelWidth)
            labelEdgeInsets = UIEdgeInsets(top: -imageHeight!-imageTitleSpace/2, left: -imageWidth!, bottom: 0, right: 0)
            break;
            
        case .Right:
            imageEdgeInsets = UIEdgeInsets(top: 0, left: labelWidth+imageTitleSpace/2, bottom: 0, right: -labelWidth-imageTitleSpace/2)
            labelEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth!-imageTitleSpace/2, bottom: 0, right: imageWidth!+imageTitleSpace/2)
            break;
        }
        self.titleEdgeInsets = labelEdgeInsets
        self.imageEdgeInsets = imageEdgeInsets
    }
    
    func setBackgroundColor(_ color:UIColor,selectedColor:UIColor) {
        self.clipsToBounds = true  // add this to maintain corner radius
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        if let context = UIGraphicsGetCurrentContext() {
            context.setFillColor(color.cgColor)
            context.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
            let colorImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            self.setBackgroundImage(colorImage, for: .normal)
        }
        
        UIGraphicsBeginImageContext(CGSize(width: 1, height: 1))
        if let context1 = UIGraphicsGetCurrentContext() {
            context1.setFillColor(selectedColor.cgColor)
            context1.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
            let colorImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            self.setBackgroundImage(colorImage, for: .selected)
        }
    }
}
